﻿using System;
using System.Linq.Expressions;
using System.Reflection;

using BrightSword.Feber.Core;

namespace BrightSword.Feber.Samples
{
    /// <summary>
    ///     This is a sample of how to use the Feber library to create a composite function taking one argument
    ///     computing a single result folding over projections of its property values
    /// </summary>
    public static class SimpleSerializer
    {
        /// <summary>
        ///     A generic method to compute an aggregate string representation of an object of type <typeparamref name="T" /> based on its property values and return it
        /// </summary>
        public static string Serialize<T>(this T _this)
        {
            return SimpleSerializer<T>.Serialize(_this);
        }
    }

    public static class SimpleSerializer<T>
    {
        private static readonly SimpleSerializerBuilder _builder = new SimpleSerializerBuilder();

        public static string Serialize(T instance)
        {
            return String.Format("{{{0}}}",
                                 _builder.Function(instance));
        }

        private class SimpleSerializerBuilder : FunctionBuilder<T, T, string>
        {
// ReSharper disable StaticFieldInGenericType
            private static readonly MethodInfo _concat = typeof (String).GetMethod("Concat",
                                                                                   new[]
                                                                                   {
                                                                                       typeof (string), typeof (string)
                                                                                   });

// ReSharper restore StaticFieldInGenericType

            protected override string Seed
            {
                get { return String.Empty; }
            }

            protected override Func<Expression, Expression, Expression> Conjunction
            {
                get
                {
                    return (_l,
                            _r) => Expression.Call(_concat,
                                                   _l,
                                                   _r);
                }
            }

            private static MethodCallExpression GetToStringExpression(PropertyInfo property,
                                                                      Expression instanceParameterExpression)
            {
                if (property.PropertyType == typeof (DateTime))
                {
                    return Expression.Call(Expression.Property(instanceParameterExpression,
                                                               property),
                                           "ToString",
                                           null,
                                           Expression.Constant("O"));
                }

                return Expression.Call(Expression.Property(instanceParameterExpression,
                                                           property),
                                       "ToString",
                                       null);
            }

            protected override Expression PropertyExpression(PropertyInfo property,
                                                             ParameterExpression instanceParameterExpression)
            {
                return Expression.Call(typeof (String),
                                       "Format",
                                       null,
                                       Expression.Constant("{0}:{1},",
                                                           typeof (string)),
                                       Expression.Constant(property.Name,
                                                           typeof (string)),
                                       GetToStringExpression(property,
                                                             instanceParameterExpression));
            }
        }
    }
}